home *** CD-ROM | disk | FTP | other *** search
- /*
- * dump.c
- * Output functions for the ntfs tools
- *
- * Copyright (C) 1995-1997 Martin von L÷wis
- * Copyright (C) 1997,1999 RΘgis Duchesne
- * Copyright (c) 1999 Richard Russon
- *
- * 1999/04/05: Merged Security Descriptor dump code from Richard --red
- */
-
- #include <errno.h>
- #include "ntfstypes.h"
- #include "struct.h"
- #include "dump.h"
- #include "config.h"
-
- #include "util.h"
- #include "nttools.h"
- #include "inode.h"
- #include "dir.h"
- #include "support.h"
- #include <stdio.h>
- #ifdef HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- #ifdef HAVE_IO_H
- #include <io.h>
- #endif
- #include <ctype.h>
- #include <stdlib.h>
- #include "macros.h"
-
- /* attribute names. Maybe this gets merged into ntfs_volume some day */
- static char* attr_names=0;
- static int attr_names_size;
-
- /* Dump a block of memory starting at buf. Display length bytes. The displayed
- index of the first byte is start */
- void dump_mem(unsigned char *buf, int start, int length)
- {
- int offs,i;
- for(offs=0;offs<length;offs+=16)
- {
- printf("%8.8X ",start+offs);
- for(i=0;i<16;i++)printf("%2X ",buf[offs+i]);
- for(i=0;i<16;i++)
- if(buf[offs+i]>31 && buf[offs+i]<128)putchar(buf[offs+i]);
- else putchar('.');
- putchar('\n');
- }
- }
-
-
-
- /* dump from the raw volume, starting at position */
- void dump(ntfs_volume *vol, ntfs_size_t position, int start, int length)
- {
- int offset;
- ntfs_lseek(NTFS_FD(vol),position,SEEK_SET);
- for(offset=0;length==-1 || offset<length;offset+=16)
- {
- int i;
- unsigned char buf[16];
- if(read(NTFS_FD(vol),buf,16)!=16)
- {perror("read");return;}
- printf("%8.8X ",start+offset);
- for(i=0;i<16;i++)
- printf("%2X ",buf[i]);
- for(i=0;i<16;i++)
- if(buf[i]>31 && buf[i]<128)putchar(buf[i]);
- else putchar('.');
- putchar('\n');
- }
- }
-
- static void
- uniprintz(char *first)
- {
- while(*first){
- putchar(*first++);
- if(*first++){
- printf("!!!!Error printing string\n");
- return;
- }
- }
- }
-
-
- /* Find a string on the volume, starting at position. If searching for Unicode
- strings, the string argument should already be Unicode */
- #define BSIZE 32768
- int grep(ntfs_volume *vol, ntfs_size_t position, int length,
- unsigned char *string, int stringlen, int ignore_case)
- {
- int offset;
- int b_offs;
- int i;
- unsigned char buf[2*BSIZE];
-
- if(ignore_case)
- for(i=0;string[i];i++)string[i]=tolower(string[i]);
- ntfs_lseek(NTFS_FD(vol),position,SEEK_SET);
- read(NTFS_FD(vol),buf,BSIZE);
- if(ignore_case)
- for(i=0;i<BSIZE;i++)buf[i]=tolower(buf[i]);
- for(offset=b_offs=0;length==-1 || offset<length;offset+=BSIZE)
- {
- if(read(NTFS_FD(vol),buf+BSIZE,BSIZE)!=BSIZE)
- {perror("read");return -1;}
- if(ignore_case)
- for(i=BSIZE;i<2*BSIZE;i++)buf[i]=tolower(buf[i]);
- for(;b_offs<BSIZE;b_offs++)
- if(buf[b_offs]==string[0])
- {
- for(i=0;i<stringlen;i++)
- if(buf[b_offs+i]!=string[i])
- break;
- if(i==stringlen)return position+offset+b_offs;
- }
- ntfs_memcpy(buf,buf+BSIZE,BSIZE);
- b_offs-=BSIZE;
- }
- return -1;
- }
-
- static int
- print_attr_type(ntfs_volume* vol,int type)
- {
- int offset,error;
- ntfs_u8 *buf=0;
- if(!attr_names){
- ntfs_attribute *data;
- ntfs_io io;
- ntfs_inode attrdef;
- error=ntfs_init_inode(&attrdef,vol,FILE_ATTRDEF);
- if(error)return error;
- offset=0;
- data=ntfs_find_attr(&attrdef,vol->at_data,NULL);
- if(!data){
- free(buf);
- return EINVAL;
- }
- buf=malloc(data->size);
- if(!buf)return ENOMEM;
- io.fn_put=ntfs_put;
- io.fn_get=ntfs_get;
- io.do_read=1;
- io.param=buf;
- io.size=4096;
- error=ntfs_readwrite_attr(&attrdef,data,offset,&io);
- if(error)return error;
- attr_names=buf;
- attr_names_size=data->size;
- }
- for(offset=0;offset<attr_names_size;offset+=0xA0)
- if(NTFS_GETU32(attr_names+offset+0x80)==type){
- uniprintz(attr_names+offset+2);
- break;
- }
- if(offset>=attr_names_size)
- printf("Unknown type");
- return 0;
- }
-
- /* print the attribute list for the MFT record at offset on the volume */
- void list_attributes(ntfs_volume *vol, ntfs_size_t offset)
- {
- char rec[4096];
- ntfs_lseek(NTFS_FD(vol),offset,SEEK_SET);
- if(read(NTFS_FD(vol),rec,sizeof(rec))!=sizeof(rec)){
- perror("read");
- return;
- }
- if(!ntfs_check_mft_record(vol,rec)){
- fprintf(stderr,"Not a mft record\n");
- return;
- }
- list_attr_mem(vol,rec);
- }
-
- /* dump the standard information attribute */
- static void dump_standard_information(ntfs_u8 *start)
- {
- printf("\tCreation time ");
- print_time(NTFS_GETU64(start));
- printf("\n\tModification time ");
- print_time(NTFS_GETU64(start + 0x8));
- printf("\n\tMFT Modification time ");
- print_time(NTFS_GETU64(start + 0x10));
- printf("\n\tAccess time ");
- print_time(NTFS_GETU64(start + 0x18));
- printf("\n");
- }
-
- /* dump the attribute list attribute */
- static void dump_attribute_list(ntfs_u8 *start, ntfs_u8 *stop)
- {
- while(start!=stop){
- printf("\tType %X,MFT# %X,Start VCN %X ",NTFS_GETU32(start),
- NTFS_GETU32(start+0x10),NTFS_GETU32(start+0x8));
- uniprint(start+0x1A,NTFS_GETU8(start+0x6));
- start+=NTFS_GETU16(start+4);
- putchar('\n');
- }
- }
-
- static void print_name(ntfs_u8 *first)
- {
- int length=*(unsigned char*)first++;
- switch(*first++)
- {
- case 0: printf("Posix-Name:");break;
- case 1: printf("Unicode-Name:");break;
- case 2: printf("DOS-Name:");break;
- case 3: printf("Unicode+DOS:");break;
- default:
- printf("Don't know how to read the name\n");
- return;
- }
- uniprint(first,length);
- puts("");
- }
-
- /* dump the filename attribute */
- static void dump_filename(ntfs_u8 *start)
- {
- printf("\t");
- print_name(start+0x40);
- printf("\tIndexed in 0x%X\n",NTFS_GETU32(start));
- }
-
- /* dump a sid */
- long dump_sid (ntfs_u8 *sid)
- {
- int revision;
- int sa_count;
- int i;
- ntfs_u64 ident_auth = 0;
-
- revision = *sid; /* Should always be 1 */
- sid++;
- sa_count = *sid; /* Number of Sub-authorities */
- sid++;
-
- for (i = 0; i < 6; i ++)
- {
- ident_auth <<= 8;
- ident_auth += *sid;
- sid++;
- }
-
- printf ("S-%d-%Lu", revision, ident_auth);
-
- for (i = 0; i < sa_count; i++)
- {
- printf ("-%u", *((ntfs_u32*) sid));
- sid += 4;
- }
-
- return 8 + (sa_count * 4);
- }
-
- void display_access_flags (ntfs_u8 flags)
- {
- if (flags & 0x1) printf (" Obj inh ACE");
- if (flags & 0x2) printf (" Con inh ACE");
- if (flags & 0x4) printf (" No prop inh");
- if (flags & 0x8) printf (" Inh onlyACE");
- }
-
- void display_access_mask (ntfs_u32 mask)
- {
- if ((mask & 0x00120089) == 0x00120089) printf ("R"); else printf (".");
- if ((mask & 0x00120116) == 0x00120116) printf ("W"); else printf (".");
- if ((mask & 0x001200a0) == 0x001200a0) printf ("X"); else printf (".");
- if ((mask & 0x00010000) == 0x00010000) printf ("D"); else printf (".");
- if ((mask & 0x00040000) == 0x00040000) printf ("P"); else printf (".");
- if ((mask & 0x00080000) == 0x00080000) printf ("O"); else printf (".");
- }
-
- void display_access_allowed_ace (ntfs_u8 *ace)
- {
- ntfs_u32 mask = NTFS_GETU32 (ace + 4);
- ntfs_u8 flags = NTFS_GETU8 (ace + 1);
-
- display_access_mask (mask);
- display_access_flags (flags);
- }
-
- void display_access_denied_ace (ntfs_u8 *ace)
- {
- ntfs_u32 mask = NTFS_GETU32 (ace + 4);
- ntfs_u8 flags = NTFS_GETU8 (ace + 1);
-
- display_access_mask (mask);
- display_access_flags (flags);
- }
-
- void display_system_audit_ace (ntfs_u8 *ace)
- {
- ntfs_u32 mask = NTFS_GETU32 (ace + 4);
- ntfs_u8 flags = NTFS_GETU8 (ace + 1);
-
- display_access_mask (mask);
-
- if (flags & 0x80) printf (" Success ");
- if (flags & 0x40) printf (" Failure ");
- }
-
- long display_ace (ntfs_u8 *ace)
- {
- ntfs_u8 type = NTFS_GETU8 (ace);
- //ntfs_u8 flags = NTFS_GETU8 (ace + 1);
- ntfs_u16 ace_size = NTFS_GETU16 (ace + 2);
- ntfs_u32 mask = NTFS_GETU32 (ace + 4);
- long sid_size = 0;
-
- switch (type)
- {
- case 0x00:
- printf ("\t\tAccess allowed: ");
- display_access_allowed_ace(ace);
- break;
-
- case 0x01:
- printf ("\t\tAccess denied: ");
- display_access_denied_ace(ace);
- break;
-
- case 0x02:
- printf ("\t\tSystem audit: ");
- display_system_audit_ace(ace);
- break;
-
- default:
- printf ("\t\tUnknown: %#010x\n", mask);
- return ace_size;
- }
-
- printf (" ");
- sid_size = dump_sid (ace + 8);
- printf ("\n");
-
- if (sid_size != ace_size) {
- // I've seen this happen
- }
-
- return ace_size;
- }
-
- long display_acl (ntfs_u8 *acl, int len)
- {
- //ntfs_u8 version = NTFS_GETU8 (acl);
- //ntfs_u8 unknown1 = NTFS_GETU8 (acl + 1);
- ntfs_u32 acl_size = NTFS_GETU16 (acl + 2);
- ntfs_u32 num_aces = NTFS_GETU16 (acl + 4);
- //ntfs_u32 unknown2 = NTFS_GETU16 (acl + 6);
-
- long l;
- long size = 8;
-
- printf (" %d entr%s\n", num_aces, num_aces == 1 ? "y" : "ies");
-
- if (acl_size < len)
- {
- for (l = 0; l < num_aces; l++)
- {
- size += display_ace (acl + size);
- }
- }
-
- return size;
- }
-
- /* dump the security descriptor attribute */
- static void dump_security_descriptor(ntfs_u8 *start, int len)
- {
- //ntfs_u32 unknown = NTFS_GETU32 (start); // This looks like flags
- ntfs_u32 off_user = NTFS_GETU32 (start + 0x4); // user SID
- ntfs_u32 off_group = NTFS_GETU32 (start + 0x8); // group SID
- ntfs_u32 off_acl_audit = NTFS_GETU32 (start + 0xC); // ACL containing audit info
- ntfs_u32 off_acl_perms = NTFS_GETU32 (start + 0x10); // ACL containing permission info
-
- /* When the Audit ACL is missing, its offset is zeroed */
- if (off_acl_audit && off_acl_audit < len) {
- printf ("\tACL - Audit");
- display_acl (start + off_acl_audit, len);
- }
- if (off_acl_perms < len) {
- printf ("\tACL - Permissions");
- display_acl (start + off_acl_perms, len);
- }
- if (off_user < len) {
- printf ("\tUser: ");
- dump_sid (start + off_user);
- printf ("\n");
- }
- if (off_group < len) {
- printf ("\tGroup: ");
- dump_sid (start + off_group);
- printf ("\n");
- }
- }
-
- /* display the list of runs of an attribute */
- static void dump_runs(unsigned char *start, int vcnstart, int vcnend,
- int compressed)
- {
- int length=0;
- ntfs_cluster_t cluster=0;
- int l=vcnend-vcnstart;
- int vcn=vcnstart;
- int ctype;
- while(l>0 && ntfs_decompress_run(&start,&length,&cluster,&ctype)!=-1)
- {
- l-=length;
- if(!ctype)
- printf("\tRun from %x to %x (VCN=%x)\n",cluster,cluster+length-1,vcn);
- else
- printf("\tCompression unit size %x\n",length);
- vcn+=length;
- }
- }
-
- /* print the attribute list at rec */
- void list_attr_mem(ntfs_volume *vol, char *rec)
- {
- int offs;
- int type, attr_length, stream_length, resident, namelen, compressed;
- char *start;
- /* first attribute should start at *0x14 */
- offs= NTFS_GETU16(rec + 0x14);
- while(offs<vol->mft_recordsize)
- {
- printf("%4.4X:",offs);
- printf("Type %X ",type=NTFS_GETU32(rec+offs));
- if(type==-1)break;
- /* offset to the next attribute */
- printf("Length %X ", attr_length = NTFS_GETU32(rec + offs + 0x4));
- resident=*(rec+offs+8)=='\0';
- compressed=*(rec+offs+0xC);
- if(resident){
- stream_length = NTFS_GETU32(rec + offs + 0x10);
- printf("resident ");
- if(*(rec+offs+0x16))
- printf("indexed ");
- }
- if(compressed)
- printf("compressed ");
- printf("Slot #%X ",NTFS_GETU16(rec+offs+14));
- /* position of attribute data if resident */
- start=rec+offs+NTFS_GETU8(rec+offs+10);
- if(NTFS_GETU8(rec+offs+10)==0)start+=NTFS_GETU16(rec+offs+0x20);
- /* length attribute name, name starts at start */
- namelen=NTFS_GETU8(rec+offs+9);
- if(namelen!=0)
- {
- printf("named(");
- uniprint(start,namelen);
- start+=namelen*2;
- printf(") ");
- }
- if(NTFS_GETU8(rec+offs+11))
- fprintf(stderr,"Found [B] at offset %X\n",offs);
- if(NTFS_GETU8(rec+offs+13))
- fprintf(stderr,"Found [D] at offset %X\n",offs);
- print_attr_type(vol,type);
- printf("\n");
- if(resident)
- printf("\tStream length 0x%X \n", stream_length);
- switch(type)
- {
- case 0x10:
- dump_standard_information(rec + offs + 0x18);
- break;
- case 0x20:
- dump_attribute_list(rec + offs + 0x18, rec + offs + 0x18 + stream_length);
- break;
- case 0x30:
- dump_filename(rec + offs + 0x18);
- break;
- case 0x50:
- if (resident)
- dump_security_descriptor(rec + offs + 0x18, stream_length);
- break;
- }
- if(!resident) {
- printf("\tAllocated %x, size %x, initialized %x",
- NTFS_GETU32(rec+offs+0x28),NTFS_GETU32(rec+offs+0x30),
- NTFS_GETU32(rec+offs+0x38));
- if(compressed)
- printf(", compressed %x\n",NTFS_GETU32(rec+offs+0x40));
- else
- putchar('\n');
- if(NTFS_GETU16(rec+offs+0x22))
- fprintf(stderr,"Found [22]=%x at offset %X\n",NTFS_GETU16(rec+offs+0x22),offs);
- if(NTFS_GETU32(rec+offs+0x24))
- fprintf(stderr,"Found [24] at offset %X\n",offs);
- dump_runs(start,NTFS_GETU32(rec+offs+0x10),
- NTFS_GETU32(rec+offs+0x18),compressed);
- }
- offs += attr_length;
- }
- puts("");
- }
-
- /* Necessary forward reference */
- static void dumpdir_entry(ntfs_inode* ino,char *entry);
-
- /* display a directory record */
- static void dumpdir_record(ntfs_inode* ino,int nextblock)
- {
- int length,error;
- char record[8192];
- char *offset;
- ntfs_io io;
-
- io.fn_put=ntfs_put;
- io.fn_get=0;
- io.param=record;
- io.size=ino->u.index.recordsize;
- error=ntfs_read_attr(ino,ino->vol->at_index_allocation,"$I30",
- nextblock*ino->vol->clustersize,&io);
- if(error || io.size!=ino->u.index.recordsize){
- printf("read failed\n");
- return;
- }
- if(!ntfs_check_index_record(ino,record)){
- printf("Not a index record\n");
- return;
- }
- offset=record+NTFS_GETU16(record+0x18)+0x18;
- do{
- dumpdir_entry(ino,offset);
- if(*(offset+0xC)&2)break;
- length=NTFS_GETU16(offset+8);
- if(!length)break;
- offset+=length;
- }while(1);
- }
-
- /* display all subentries, then display this entry */
- static void dumpdir_entry(ntfs_inode* ino,char *entry)
- {
- int length=NTFS_GETU16(entry+8);
- int used=(NTFS_GETU8(entry+12)&2)==0;
- if(used)printf("\tinode %x\t",NTFS_GETU32(entry));
- if(NTFS_GETU8(entry+13))
- fprintf(stderr,"Found [D] at %x\n",ino->i_number);
- if(NTFS_GETU16(entry+14))
- fprintf(stderr,"Found [E] at %x\n", ino->i_number);
- if((int)NTFS_GETU8(entry+12)&1){
- int nextblock=NTFS_GETU64(entry+length-8);
- printf("Going down to block %x\n",nextblock);
- dumpdir_record(ino,nextblock);
- printf("back to\tinode %x\t",NTFS_GETU32(entry));
- }
- if(used)print_name(entry+0x50);
- }
-
- /* display an inode as directory */
- void dumpdir(ntfs_inode *ino)
- {
- int length=ino->vol->mft_recordsize;
- char *buf=(char*)malloc(length);
- char *data;
- ntfs_io io;
-
- io.fn_put=ntfs_put;
- io.fn_get=0;
- io.param=buf;
- io.size=length;
- if(ntfs_read_attr(ino,ino->vol->at_index_root,"$I30",0,&io))
- {
- printf("Not a directory\n");
- free(buf);
- return;
- }
- ino->u.index.recordsize=NTFS_GETU32(buf+0x8);
- ino->u.index.clusters_per_record=NTFS_GETU32(buf+0xC);
- /* FIXME: consistency check */
- data=buf+0x20;
- while(1)
- {
- length=NTFS_GETU16(data+8);
- dumpdir_entry(ino,data);
- if(NTFS_GETU8(data+12)&2)break;
- data+=length;
- if(!length){
- printf("length==0!!\n");
- break;
- }
- }
- free(buf);
- }
-
- #if 0
- static void putchar1(unsigned char c)
- {
- if(c>=32 && c<=127)
- putchar(c);
- else switch(c)
- {
- case 10: printf("\\n");break;
- case 13: printf("\\r");break;
- default: printf("\\%o",c);
- }
- }
- #endif
-
- void dump_decompress(ntfs_inode *ino, int run, int verbose)
- {
- printf("Function is obsolete\n");
- /* FIXME: really? */
- #if 0
- int block,len,clear_pos;
- unsigned char *compressed;
- char clear[16384];
- int tag=0,bits,charmode;
- int ctype;
- unsigned char *data;
- int offset=0;
- unsigned char *attr=ntfs_get_attr(ino,AT_DATA,0);
- ntfs_io io;
-
- io.fn_put=ntfs_put;
- io.fn_get=0;
- if(!attr)
- {
- fprintf(stderr,"No data attribute in this inode\n");
- return;
- }
- if(attr->resident)
- {
- fprintf(stderr,"Attribute is resident\n");
- return;
- }
- if(!attr->compressed)
- {
- fprintf(stderr,"Data attribute is not compressed\n");
- return;
- }
- /*Skip name and valueoffset*/
- attr+=NTFS_GETU16(attr+0x20)+NTFS_GETU8(attr+9);
- block=0;
- do{
- ntfs_decompress_run(&attr,&len,&block,&ctype);
- }while(run--);
- compressed=(char*)malloc(len*vol->clustersize);
- io.param=compressed;
- io.do_read=1;
- error=ntfs_getput_clusters(vol,block,0,
- len*vol->clustersize,&io);
- if(error)
- fprintf(stderr,"Error reading block %x\n",block);
- data=compressed;
- while(*(data+1) & 0xF0)
- {
- int block_size;
- unsigned char *stop;
- block_size = *(unsigned short*)data;
- if(verbose)printf("Head %x",block_size);
- block_size &= 0xFFF;
- data+=2;
- offset+=2;
- stop = data + block_size;
- bits=0;
- charmode=0;
- clear_pos=0;
- while(data<=stop)
- {
- if(!bits){
- if(verbose)printf("\nOffset %x",offset);
- charmode=0;
- tag=*data;
- bits=8;
- data++;
- offset++;
- if(data>stop)
- break;
- }
- if(tag&1){
- int i,len,delta,delta1=0;
- delta = *(unsigned short*)(data);
- len=*data;
- len&=0x1f;
- if(clear_pos<=0x10)
- {
- delta1=delta>>12;
- len = delta & 0xFFF;
- }
- else if(clear_pos<=0x20)
- {
- delta1=delta>>11;
- len = delta & 0x7FF;
- }
- else if(clear_pos<=0x40)
- {
- delta1=delta>>10;
- len = delta & 0x3FF;
- }
- else if(clear_pos<=0x80)
- {
- delta1=delta>>9;
- len = delta & 0x1FF;
- }
- else if(clear_pos<=0x100)
- {
- delta1=delta>>8;
- len = delta & 0xFF;
- }
- else if(clear_pos<=0x200)
- {
- delta1=delta>>7;
- len = delta & 0x7F;
- }
- else if(clear_pos<=0x400)
- {
- delta1=delta>>6;
- len = delta & 0x3F;
- }
- else if(clear_pos<=0x800)
- {
- delta1=delta>>5;
- len = delta & 0x1F;
- }
- else if(clear_pos<=0x1000)
- {
- delta1=delta>>4;
- len = delta & 0xF;
- }else
- fprintf(stderr,"NOW WHAT?\n");
- len+=3;
- if(verbose)printf("\n%8.8X:len %x(%x) delta %x(%x) ",
- clear_pos,len,*data,delta,delta1);
- charmode=0;
- for(i=0;i<len;i++)
- {
- if(verbose)
- putchar1(clear[clear_pos-delta1-1]);
- else
- putchar(clear[clear_pos-delta1-1]);
- clear[clear_pos]=clear[clear_pos-delta1-1];
- clear_pos++;
- }
- data+=2;
- offset+=2;
- }else{
- if(verbose)if(!charmode)
- printf("\n%8.8X:",clear_pos);
- if(verbose)
- putchar1(*data);
- else
- putchar(*data);
- clear[clear_pos++]=*data;
- data++;
- offset++;
- charmode=1;
- }
- tag>>=1;
- bits--;
- }
- if(verbose)putchar('\n');
- }/*while*/
- #endif
- }
-
- void dump_inode(ntfs_inode *ino)
- {
- int i,j,vcn;
- printf("Inode %d, %d attributes, %d mft clusters\n",
- ino->i_number, ino->attr_count, ino->record_count);
- for(i=0;i<ino->attr_count;i++)
- {
- printf("attribute %X",ino->attrs[i].type);
- if(ino->attrs[i].name)
- {
- printf(" named(");
- uniprint((char*)ino->attrs[i].name,ino->attrs[i].namelen);
- printf(")\n");
- }else
- printf("\n");
- if(ino->attrs[i].resident)
- printf(" resident\n");
- else
- for(j=0,vcn=0;j<ino->attrs[i].d.r.len;j++)
- {
- printf(" Run %d from %x len %x (VCN=%x)\n", j,
- ino->attrs[i].d.r.runlist[j].cluster,
- ino->attrs[i].d.r.runlist[j].len,vcn);
- vcn+=ino->attrs[i].d.r.runlist[j].len;
- }
- }
- }
- /*
- * Local variables:
- * c-file-style: "linux"
- * End:
- */
-